﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Security.Cryptography;

namespace TCPServer
{
    public class Communication
    {
        private String STRING_DISCONNECT_MESSAGE = "offline.";
        private String STRING_CHANGE_NAME = "Please change your user name, there is a confliection.";
        private String STRING_WELCOME_MESSAGE = "Welcome";
        private String STRING_SAYS = " says:";

        private Thread _incomingConnectThread;     //声明一个线程实例
        private Socket _listeningSocket;           //声明一个Socket实例
        private bool _isListening;
        private int _clientNumber;
        private int _localPort;
        private Byte[] _msgBuffer;                  //存放消息数据
        private IPEndPoint _localEndPoint;
        //*****
        private Hashtable _sharedSecret;
        private Hashtable _socketDataList;
        private DiffieHellman _diffieHellmanServer;
        private int _keyBitLength;
        //*****
        public delegate void UserListEventHandler(String changeClient,bool changeAD);
        public event UserListEventHandler UserListChange = null;
        public delegate void MessageListEventHandler(String changeClient);
        public event MessageListEventHandler MessgaeListChange = null;
        public delegate void ClearListEventHandler();
        public event ClearListEventHandler ClearListChange = null;

        public Communication()
        {
            _isListening = false;
            _clientNumber = 0;
            _sharedSecret = new Hashtable();
            _socketDataList = new Hashtable();
            _msgBuffer = new byte[65535];//消息数据大小
            _keyBitLength = 64;
        }

        public void IntializeService(String port)
        {
            LocalPort = int.Parse(port);//设置端口
            _listeningSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//初始化SOCKET实例
            _listeningSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);//允许SOCKET被绑定在已使用的地址上。
            _localEndPoint = new IPEndPoint(IPAddress.Any, LocalPort);//初始化终结点实例
        }

        public void StartTread()
        {
            _listeningSocket.Bind(_localEndPoint);//绑定
            _listeningSocket.Listen(10);//监听

            ThreadStart myThreadDelegate = new ThreadStart(ReciveAccept);
            _incomingConnectThread = new Thread(myThreadDelegate);

            _isListening = true;
            _incomingConnectThread.Start();
        }
        //接受客户端连接的方法
        private void ReciveAccept()
        {
            Socket tempSocket;
            SocketDataType tempSocketData;
            String clientFirstMessgae;//dfMsg
            String[] words;
            char[] delimiterChars = { ',' };
            int REnd;

            while (_isListening)
            {
                try
                {
                    tempSocket = _listeningSocket.Accept();
                    REnd = tempSocket.Receive(_msgBuffer, 0, _msgBuffer.Length, SocketFlags.None);
                    clientFirstMessgae = Encoding.UTF8.GetString(_msgBuffer, 0, REnd);
                    words = SplitString(clientFirstMessgae, delimiterChars);
                    if (_msgBuffer.Length != 0)
                    {
                        //Console.WriteLine(clientFirstMessgae);         //test debug
                    }
                    _diffieHellmanServer = new DiffieHellman(_keyBitLength).GenerateResponse(words[0].ToString());
                    SingleMessageSend(tempSocket, _diffieHellmanServer.ToString());
                    Console.WriteLine("DiffieHellman with " + words[1].ToString() + " : " + _diffieHellmanServer.ToString());
                    Console.WriteLine("Shared-Key with " + words[1].ToString() + " : " + Convert.ToBase64String(_diffieHellmanServer.Key));

                    if (_socketDataList.ContainsKey(words[1].ToString()))
                    {
                        SingleMessageSend(tempSocket, STRING_CHANGE_NAME, _diffieHellmanServer.Key);
                        tempSocket.Close();
                        tempSocket = null;
                    }
                    else
                    {
                        UserListChange.Invoke((tempSocket.RemoteEndPoint.ToString() + "," + words[1].ToString()), true);
                        tempSocketData = new SocketDataType(tempSocket, 
                                                            tempSocket.RemoteEndPoint, 
                                                            words[1].ToString(),
                                                            _diffieHellmanServer.Key);
                        _socketDataList.Add(words[1], tempSocketData);
                        (((SocketDataType)_socketDataList[words[1]]).SocketData).BeginReceive(_msgBuffer,
                                                                                 0, _msgBuffer.Length, SocketFlags.None,
                                                                                 new AsyncCallback(ReciveCallBack),
                                                                                 ((SocketDataType)_socketDataList[words[1]]).SocketData);
                        _clientNumber++;
                        SingleMessageSend(tempSocket, STRING_WELCOME_MESSAGE, _diffieHellmanServer.Key);
                    }
                }
                catch (Exception e)
                { }
            }
        }

        private void ReciveCallBack(IAsyncResult AR)
        {
            String[] words;
            Byte[] brocastData;
            Byte[] encryptedData;//待解密的data
            Byte[] decryptedData;
            String realMsgReceive;
            String messageToBrocast;
            Byte[] key;
            char[] delimiterChars = { ',' };

            try
            {
                Socket RSocket = (Socket)AR.AsyncState;
                int REnd = RSocket.EndReceive(AR);
                String receiveString = Encoding.UTF8.GetString(_msgBuffer, 0, REnd);
                words = SplitString(receiveString, delimiterChars);

                Console.WriteLine("data receive : " + receiveString);
                encryptedData = System.Text.Encoding.UTF8.GetBytes(words[1].ToString());
                key = ((SocketDataType)_socketDataList[words[0]]).KeyData;
                decryptedData = DESFactory.DecryptDES(encryptedData, key);//解密
                realMsgReceive = Encoding.UTF8.GetString(decryptedData);
                messageToBrocast = words[0].ToString() + STRING_SAYS + realMsgReceive;

                MessgaeListChange.Invoke(messageToBrocast);
                brocastData = Encoding.UTF8.GetBytes(messageToBrocast);

                if (realMsgReceive.Contains(STRING_DISCONNECT_MESSAGE))
                {
                    UserListChange.Invoke((RSocket.RemoteEndPoint.ToString() + "," + words[0].ToString()), false);
                    //((SocketDataType)_socketDataList[words[0]]).SocketData.EndReceive(AR);
                    ((SocketDataType)_socketDataList[words[0]]).SocketData.Close();
                    _socketDataList.Remove(words[0]);
                    _clientNumber--;
                }

                foreach (SocketDataType tempClientData in _socketDataList.Values)
                {
                    if (tempClientData.SocketData.Connected)
                    {
                        encryptedData = BrocastSendEach(brocastData, tempClientData);
                        RSocket.BeginReceive(_msgBuffer, 0, _msgBuffer.Length, 0, new AsyncCallback(ReciveCallBack), RSocket);
                    }
                }
            }
            catch (Exception e)
            { }
        }

        public void Offline()
        {
            if (_isListening)
            {   
                ClearListChange.Invoke();
                _listeningSocket.Close();
               
                foreach (SocketDataType tempClientData in _socketDataList.Values)
                {
                    if (tempClientData != null)
                    {
                        ((Socket)tempClientData.SocketData).Close();
                        
                    }
                }
                _incomingConnectThread.Interrupt();
                _incomingConnectThread.Abort();
            }
            _socketDataList.Clear();
            _isListening = false;
            _clientNumber = 0;
        }
        //with des
        public void SingleMessageSend(Socket tempSocket, String messgaeToSend)
        {
            Byte[] data = Encoding.UTF8.GetBytes(messgaeToSend);
            tempSocket.Send(data, 0, data.Length, SocketFlags.None);
        }
        //no des
        public void SingleMessageSend(Socket tempSocket, String messgaeToSend, Byte[] key)
        {
            Byte[] encryptedData = null;
            encryptedData = DESFactory.EncryptDES(Encoding.UTF8.GetBytes(messgaeToSend), key);
            tempSocket.Send(encryptedData, 0, encryptedData.Length, SocketFlags.None);
        }
        //with des
        public void BrocastMessage(String messageToSend)
        {
            Byte[] brocastData = Encoding.UTF8.GetBytes(messageToSend);
            Byte[] encryptedData = null;
            MessgaeListChange.Invoke(messageToSend);

            foreach (SocketDataType tempClientData in _socketDataList.Values)
            {
                if (((Socket)tempClientData.SocketData).Connected)
                {
                    encryptedData = BrocastSendEach(brocastData, tempClientData);
                }
            }
        }

        private Byte[] BrocastSendEach(Byte[] brocastData, SocketDataType tempClientData)
        {
            Byte[] encryptedData = null;
            if (((Socket)tempClientData.SocketData).Connected)
            {
                encryptedData = DESFactory.EncryptDES(brocastData, tempClientData.KeyData);
<<<<<<< .mine
               // Console.WriteLine(encryptedData.Length);
=======
                //Console.WriteLine(encryptedData.Length);             //test debug
>>>>>>> .r31
                ((Socket)tempClientData.SocketData).Send(encryptedData, 0, encryptedData.Length, SocketFlags.None);
            }
            return encryptedData;
        }

        private static String[] SplitString(String clientFirstMessgae, char[] delimiterChars)
        {
            String[] words;
            words = clientFirstMessgae.Split(delimiterChars);
            return words;
        }

        public int LocalPort
        {
            get { return _localPort; }
            set { _localPort = value; }
        }

        public int ClientNumber
        {
            get { return _clientNumber; }
            set { _clientNumber = value; }
        }

        public bool IsListening
        {
            get { return _isListening; }
            set { _isListening = value; }
        }
    }
}
